#include <retro.h>
#include "player.frame.h"
#include "player.api.h"
#include "player.graphicbuffer.h"
#include "player.texture.h"
#include "player.font.h"
#include "player.sprite.h"
#include "player.gridmap.h"
#include "player.spritemap.h"
#include "player.hittest.h"

using namespace Retro::Player;
using namespace Retro::Player::Graphic;
using namespace Retro::Scripting;

Data::DataManager*  gDataManager = NULL;
GraphicBuffer*      gGraphBuffer = NULL;

Data::Resource* FindResource(
  ScriptInterface* iScript,
  UINT iIndex,
  Data::ResourceType iType
  )
{
  Data::Resource* wResource = NULL;

  switch (iScript->getType(iIndex))
  {
  case TypeString:
    wResource = Frame::get()->getProject().findResource(
      iType, iScript->getString(iIndex));
    break;
  case TypeNumber:
    wResource = Frame::get()->getProject().findResource(
      iType, iScript->getInt(iIndex));
    break;
  }

  return wResource;
}

template <class T>
INL BOOL GetGraphic(T*& oGraphic, UINT iIndex, GraphicType iType)
{
  if (iIndex >= 0 && iIndex < gGraphBuffer->getLength() &&
    gGraphBuffer->getType(iIndex) == iType)
  {
    oGraphic = (T*)(*gGraphBuffer)[iIndex];
    return TRUE;
  }

  API_InvalidArgument();
  return FALSE;
}

API_DeclareFunction(GetCursorPos)
{
  Geom::Point wPos = Geom::Point::fromCursor();

  iScript->push(wPos.x);
  iScript->push(wPos.y);
}

API_DeclareFunction(Run)
{
  Data::Script* wScript = (Data::Script*)
    FindResource(iScript, 1, Data::ResourceScript);

  iScript->pushBool(wScript ? iScript->run(wScript->getBuffer()) : FALSE);
}

API_DeclareFunction(HitTestRect)
{
  HitTest::HitTestData wHitTest = {
    Vector2D(iScript->getFloat(9), iScript->getFloat(10))
  };

  HitTest::HitTestBounds(
    iScript->getBounds(1),
    iScript->getBounds(5),
    wHitTest);

  iScript->push(wHitTest.vel.x);
  iScript->push(wHitTest.vel.y);
  iScript->push(wHitTest.dirX);
  iScript->push(wHitTest.dirY);
}

API_DeclareFunction(HitTestSprite)
{
  SpriteBuffer* wGraphic = NULL;
  UINT wIndex = (UINT)iScript->getInt(2);

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer) &&
    wIndex >= 0 && wIndex < wGraphic->getSize())
  {
    HitTest::HitTestData wHitTest;

    iScript->push(wGraphic->hitTestSprite(wIndex, wHitTest));
    iScript->push(wHitTest.dirX);
    iScript->push(wHitTest.dirY);
  }
}

API_DeclareFunction(HitTestMap)
{
  SpriteBuffer* wGraphic = NULL;
  GridMap* wMap = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer) &&
      GetGraphic(wMap, (UINT)iScript->getInt(3), GraphicGridMap))
  {
    HitTest::HitTestData wHitTest;

    iScript->push(wGraphic->hitTestMap(
      (UINT)max(0, iScript->getInt(2)),
      wMap->getMap(),
      (UINT)max(0, iScript->getInt(4)),
      wHitTest));
    iScript->push(wHitTest.dirX);
    iScript->push(wHitTest.dirY);
  }
}

API_DeclareFunction(SetDisplayMode)
{
  Display::DisplayMode wMode = (Display::DisplayMode)iScript->getInt(1);

  switch (wMode)
  {
  case Display::DisplayWindowed:
    Frame::get()->setFullscreen(FALSE);
    break;
  case Display::DisplayFullscreen:
    Frame::get()->setFullscreen(TRUE);
    break;
  }
}

API_DeclareFunction(GetDisplayMode)
{
  iScript->push(Frame::get()->getDisplay()->getDisplayMode());
}

API_DeclareFunction(GetGridMapSize)
{
  GridMap* wMap = NULL;

  if (GetGraphic(wMap, (UINT)iScript->getInt(1), GraphicGridMap) &&
    wMap->getMap() && wMap->getTexture())
  {
    INT wTileSize = wMap->getTexture()->getDataTileset().tileSize;

    iScript->push(wMap->getMap()->getWidth() * wTileSize);
    iScript->push(wMap->getMap()->getHeight() * wTileSize);
    iScript->push(wMap->getMap()->getLayers());
    return;
  }

  iScript->push(0);
  iScript->push(0);
  iScript->push(0);
}

API_DeclareFunction(GetSpriteMapSize)
{
  SpriteMap* wMap = NULL;

  if (GetGraphic(wMap, (UINT)iScript->getInt(1), GraphicSpriteMap) &&
    wMap->getMap())
  {
    iScript->push(wMap->getMap()->getWidth());
    iScript->push(wMap->getMap()->getHeight());
    iScript->push(wMap->getMap()->getLayers());
    return;
  }

  iScript->push(0);
  iScript->push(0);
  iScript->push(0);
}

API_DeclareFunction(GetGridMapTexture)
{
  GridMap* wMap = NULL;

  INT wX = iScript->getInt(2);
  INT wY = iScript->getInt(3);
  INT wL = iScript->getInt(4);

  if (GetGraphic(wMap, (UINT)iScript->getInt(1), GraphicGridMap) &&
    wMap->getMap() &&
    wX >= 0 && wX < wMap->getMap()->getWidth() &&
    wY >= 0 && wY < wMap->getMap()->getHeight() &&
    wL >= 0 && wL < wMap->getMap()->getLayers())
  {
    iScript->push(wMap->getMap()->getGrid()[wL][
      wX + wY * wMap->getMap()->getWidth()].texture);
    return;
  }

  iScript->push(-1);
}

API_DeclareFunction(GetScreenSize)
{
  iScript->push(Frame::get()->getDisplay()->getWidth());
  iScript->push(Frame::get()->getDisplay()->getHeight());
}

API_DeclareFunction(CalculateFPS)
{
  FPSCounter* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicFPSCounter))
    wGraphic->calcFPS();
}

API_DeclareFunction(IsMapLayerVisible)
{
  INT wLayer = iScript->getInt(2);

  GridMap* wGraphic = NULL;

  GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicGridMap);

  iScript->pushBool(wGraphic && wGraphic->getMap() ?
    wGraphic->getLayerDisplay()[wLayer] : FALSE);
}

API_DeclareFunction(SetMapLayerVisible)
{
  INT wLayer = iScript->getInt(2);
  GridMap* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicGridMap) &&
    wLayer >= 0 && wLayer < wGraphic->getMap()->getLayers())
    wGraphic->getLayerDisplay()[wLayer] = iScript->getBool(3);
}

API_DeclareFunction(CreateTexture)
{
  Data::Texture* wResource = (Data::Texture*)
    FindResource(iScript, 1, Data::ResourceTexture);

  if (!wResource)
  {
    API_InvalidArgument();
    iScript->pushNull();
    return;
  }

  Display::Texture* wTexture =
    gGraphBuffer->getDisplay()->createTexture(wResource->getPath());

  Texture* wGraphic = new Texture();

  wGraphic->setTexture(wTexture);
  iScript->push(gGraphBuffer->add(wGraphic, GraphicTexture));

  if (wTexture) wTexture->unref();
}

API_DeclareFunction(CreateGridMap)
{
  Data::GridMap* wResource = (Data::GridMap*)
    FindResource(iScript, 1, Data::ResourceGridMap);

  if (!wResource)
  {
    API_InvalidArgument();
    iScript->pushNull();
    return;
  }

  GridMap* wMap = new GridMap();

  wMap->setMap(wResource);
  iScript->push(gGraphBuffer->add(wMap, GraphicGridMap));
}

API_DeclareFunction(CreateSpriteMap)
{
  Data::SpriteMap* wResource = (Data::SpriteMap*)
    FindResource(iScript, 1, Data::ResourceSpriteMap);

  if (!wResource)
  {
    API_InvalidArgument();
    iScript->pushNull();
    return;
  }

  SpriteMap* wMap = new SpriteMap();

  wMap->setMap(wResource);
  iScript->push(gGraphBuffer->add(wMap, GraphicSpriteMap));
}

API_DeclareFunction(CreateFPSCounter)
{
  iScript->push(gGraphBuffer->add(new FPSCounter(), GraphicFPSCounter));
}

API_DeclareFunction(CreateFont)
{
  Font* wFont = new Font();

  wFont->setFont(gGraphBuffer->getDisplay()->createFont(
    iScript->getString(1),  // facename
    iScript->getInt(2),     // height
    iScript->getInt(3),     // weight
    iScript->getBool(4)));  // italic

  iScript->push(gGraphBuffer->add(wFont, GraphicFont));
}

API_DeclareFunction(CreateSpriteSet)
{
  Data::Texture* wResource = (Data::Texture*)
    FindResource(iScript, 1, Data::ResourceTexture);

  if (!wResource)
  {
    API_InvalidArgument();
    iScript->pushNull();
    return;
  }

  Display::Texture* wTexture =
    gGraphBuffer->getDisplay()->createTexture(wResource->getPath());

  SpriteSet* wGraphic = new SpriteSet();

  wGraphic->setTexture(wTexture);
  wGraphic->setSpriteSize((UINT)iScript->getInt(2));
  wGraphic->setFrameCount((UINT)iScript->getInt(3));

  iScript->push(gGraphBuffer->add(wGraphic, GraphicSpriteSet));

  if (wTexture) wTexture->unref();
}

API_DeclareFunction(CreateSpriteBuffer)
{
  SpriteBuffer* wGraphic = new SpriteBuffer();

  wGraphic->setSize((UINT)iScript->getInt(1));
  iScript->push(gGraphBuffer->add(wGraphic, GraphicSpriteBuffer));
}

API_DeclareFunction(SetSpriteBufferSize)
{
  SpriteBuffer* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer))
    wGraphic->setSize((UINT)iScript->getInt(2));
}

API_DeclareFunction(GetSpriteBufferSize)
{
  SpriteBuffer* wGraphic = NULL;

  GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer);

  iScript->push(wGraphic ? (INT)wGraphic->getSize() : -1);
}

API_DeclareFunction(RenderSpriteBuffer)
{
  SpriteBuffer* wBuffer = NULL;
  SpriteSet*    wSpriteSet = NULL;

  if (GetGraphic(wBuffer, (UINT)iScript->getInt(1), GraphicSpriteBuffer) &&
      GetGraphic(wSpriteSet, (UINT)iScript->getInt(2), GraphicSpriteSet))
  {
    wBuffer->render(
      gGraphBuffer->getDisplay(),
      iScript->getPoint(3),
      wSpriteSet,
      iScript->getInt(5),
      iScript->getInt(6),
      (SpritePart)iScript->getInt(7));
  }
}

API_DeclareFunction(GetSprite)
{
  SpriteBuffer* wGraphic = NULL;
  UINT wIndex = (UINT)iScript->getInt(2);

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer) &&
    wIndex >= 0 && wIndex < wGraphic->getSize())
  {
    Sprite& wSprite = wGraphic->getSprite(wIndex);

    ScriptValue wBounds[] = {
      { &wSprite.bounds.x,    TypeInteger,  "x"       },
      { &wSprite.bounds.y,    TypeInteger,  "y"       },
      { &wSprite.bounds.cx,   TypeInteger,  "cx"      },
      { &wSprite.bounds.cy,   TypeInteger,  "cy"      },
    };

    ScriptValue wBBox[] = {
      { &wSprite.bbox.x,      TypeInteger,  "x"       },
      { &wSprite.bbox.y,      TypeInteger,  "y"       },
      { &wSprite.bbox.cx,     TypeInteger,  "cx"      },
      { &wSprite.bbox.cy,     TypeInteger,  "cy"      },
    };

    ScriptValue wVel[] = {
      { &wSprite.vel.x,       TypeFloat,    "x"       },
      { &wSprite.vel.y,       TypeFloat,    "y"       },
    };

    ScriptValue wValues[] = {
      API_MakeObject(wBounds,               "bounds"  ),
      API_MakeObject(wBBox,                 "bbox"    ),
      API_MakeObject(wVel,                  "vel"     ),
      { &wSprite.index,       TypeUnsigned, "index"   },
      { &wSprite.frame,       TypeUnsigned, "frame"   },
      { &wSprite.dir,         TypeInteger,  "dir"     },
    };

    iScript->push(wValues, ARRAYSIZE(wValues));
    return;
  }

  API_InvalidArgument();
  iScript->pushNull();
}

API_DeclareFunction(SetSprite)
{
  SpriteBuffer* wGraphic = NULL;
  UINT wIndex = (UINT)iScript->getInt(2);

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer) &&
    wIndex >= 0 && wIndex < wGraphic->getSize())
  {
    Sprite& wSprite = wGraphic->getSprite(wIndex);

    wSprite.bounds  = iScript->getBounds(3);
    if (iScript->getParamCount() == 6)  return;

    wSprite.bbox    = iScript->getBounds(7);
    if (iScript->getParamCount() == 10) return;

    wSprite.vel.x   = (REAL)iScript->getDouble(11);
    wSprite.vel.y   = (REAL)iScript->getDouble(12);
    if (iScript->getParamCount() == 12) return;

    wSprite.dir     = (Direction)iScript->getInt(13);
    if (iScript->getParamCount() == 13) return;

    wSprite.frame   = iScript->getInt(14);
    if (iScript->getParamCount() == 14) return;

    wSprite.index   = iScript->getInt(15);
  }
}

API_DeclareFunction(SetSpriteVelocity)
{
  SpriteBuffer* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer))
  {
    if (iScript->getParamCount() == 5)
    {
      UINT wBegin = (UINT)max(0, iScript->getInt(2));
      UINT wEnd   = (UINT)max((INT)wBegin, iScript->getInt(3));
      REAL wVelX  = (REAL)iScript->getDouble(4);
      REAL wVelY  = (REAL)iScript->getDouble(5);

      for (UINT i = wBegin; i < wEnd; ++i)
      {
        Sprite& wSprite = wGraphic->getSprite(i);

        wSprite.vel.x   = wVelX;
        wSprite.vel.y   = wVelY;
      }
    }
    else
    {
      Sprite& wSprite = wGraphic->getSprite((UINT)iScript->getInt(2));

      wSprite.vel.x   = (REAL)iScript->getDouble(3);
      wSprite.vel.y   = (REAL)iScript->getDouble(4);
    }
  }
}

API_DeclareFunction(SetSpriteBounds)
{
  SpriteBuffer* wGraphic = NULL;
  UINT wIndex = (UINT)iScript->getInt(2);

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer) &&
    wIndex >= 0 && wIndex < wGraphic->getSize())
  {
    Sprite& wSprite = wGraphic->getSprite(wIndex);

    wSprite.bounds  = iScript->getBounds(3);
  }
}

API_DeclareFunction(SetSpriteBoundingBox)
{
  SpriteBuffer* wGraphic = NULL;
  UINT wIndex = (UINT)iScript->getInt(2);

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer) &&
    wIndex >= 0 && wIndex < wGraphic->getSize())
  {
    Sprite& wSprite = wGraphic->getSprite(wIndex);

    wSprite.bbox    = iScript->getBounds(3);
  }
}

API_DeclareFunction(SetSpriteAttributes)
{
  SpriteBuffer* wGraphic = NULL;
  UINT wIndex = (UINT)iScript->getInt(2);

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer) &&
    wIndex >= 0 && wIndex < wGraphic->getSize())
  {
    INT wParams     = iScript->getParamCount();
    Sprite& wSprite = wGraphic->getSprite(wIndex);

    wSprite.dir     = (Direction)iScript->getInt(3);
    if (wParams == 3) return;

    wSprite.frame   = max(0, iScript->getInt(4));
    if (wParams == 4) return;

    wSprite.index   = max(0, iScript->getInt(5));
  }
}

API_DeclareFunction(UpdateSpriteBuffer)
{
  SpriteBuffer* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteBuffer))
  {
    for (UINT i = 0; i < wGraphic->getSize(); ++i)
    {
      Sprite& wSprite = wGraphic->getSprite(i);

      wSprite.bounds.x += (LONG)ceil(abs(wSprite.vel.x)) *
        (wSprite.vel.x < 0 ? -1 : 1);
      wSprite.bounds.y += (LONG)ceil(abs(wSprite.vel.y)) *
        (wSprite.vel.y < 0 ? -1 : 1);
    }
  }
}

API_DeclareFunction(RenderRect)
{
  gGraphBuffer->getDisplay()->beginBatch(NULL);

  switch (iScript->getParamCount())
  {
  case 5:
    iScript->pushBool(gGraphBuffer->getDisplay()->addBatch(
      Geom::Bounds::get(
      iScript->getInt(1),
      iScript->getInt(2),
      iScript->getInt(3),
      iScript->getInt(4)
      ),
      &Geom::Bounds::get(0, 0, 0, 0),
      iScript->getColor(5)
      ));
    break;
  case 8:
    iScript->pushBool(gGraphBuffer->getDisplay()->addBatch(
      Geom::Bounds::get(
      iScript->getInt(1),
      iScript->getInt(2),
      iScript->getInt(3),
      iScript->getInt(4)
      ),
      &Geom::Bounds::get(0, 0, 0, 0),
      iScript->getColor(5),
      iScript->getColor(6),
      iScript->getColor(7),
      iScript->getColor(8)
      ));
    break;
  default:
    API_InvalidArgument();
  }
}

API_DeclareFunction(RenderString)
{
  Font* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicFont))
  {
    gGraphBuffer->getDisplay()->beginFont();

    iScript->pushBool(gGraphBuffer->getDisplay()->drawString(
      wGraphic->getFont(),
      iScript->getString(2),
      iScript->getInt(7),
      iScript->getColor(8),
      iScript->getBounds(3)));
  }
}

API_DeclareFunction(GetFPS)
{
  FPSCounter* wGraphic = NULL;

  GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicFPSCounter);

  iScript->push(wGraphic ? (INT)ceil(wGraphic->getFPS()) : -1);
}

API_DeclareFunction(RenderGridMap)
{
  GridMap* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicGridMap))
  {
    wGraphic->render(gGraphBuffer->getDisplay(),
      iScript->getInt(2),
      iScript->getInt(3));
  }
}

API_DeclareFunction(RenderSpriteMap)
{
  SpriteMap* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicSpriteMap))
  {
    wGraphic->render(gGraphBuffer->getDisplay(),
      iScript->getInt(2),
      iScript->getInt(3));
  }
}

API_DeclareFunction(RenderTexture)
{
  Texture* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicTexture))
  {
    INT wCount = iScript->getParamCount();

    switch (wCount)
    {
    case 3:   // t, dx, dy
    case 4:   // t, dx, dy, c
      wGraphic->render(
        gGraphBuffer->getDisplay(),
        Geom::Bounds::get(
        iScript->getInt(2),
        iScript->getInt(3),
        wGraphic->getWidth(),
        wGraphic->getHeight()),
        Geom::Bounds::get(0, 0, wGraphic->getWidth(), wGraphic->getHeight()),
        wCount == 3 ? 0xFFFFFFFF : iScript->getColor(4));
      break;
    case 5:   // t, dx, dy, dw, dh
    case 6:   // t, dx, dy, dw, dh, c
      wGraphic->render(
        gGraphBuffer->getDisplay(),
        iScript->getBounds(2),
        Geom::Bounds::get(0, 0, wGraphic->getWidth(), wGraphic->getHeight()),
        wCount == 5 ? 0xFFFFFFFF : iScript->getColor(6));
      break;
    case 7:   // t, dx, dy, sx, sy, sw, sh
    case 8:   // t, dx, dy, sx, sy, sw, sh, c
      wGraphic->render(
        gGraphBuffer->getDisplay(),
        Geom::Bounds::get(
        iScript->getInt(2),
        iScript->getInt(3),
        iScript->getInt(6),
        iScript->getInt(7)),
        iScript->getBounds(4),
        wCount == 7 ? 0xFFFFFFFF : iScript->getColor(8));
      break;
    case 9:   // t, dx, dy, dw, dh, sx, sy, sw, sh
    case 10:  // t, dx, dy, dw, dh, sx, sy, sw, sh, c
      wGraphic->render(
        gGraphBuffer->getDisplay(),
        iScript->getBounds(2),
        iScript->getBounds(6),
        wCount == 9 ? 0xFFFFFFFF : iScript->getColor(10));
      break;
    default:
      API_InvalidArgument();
    }
  }
}

API_DeclareFunction(DestroyGraphic)
{
  gGraphBuffer->remove(iScript->getInt(1));
}

API_DeclareFunction(ResetTransform)
{
  Display::DisplayDevice& wDevice = *Frame::get()->getDisplay();
  wDevice.resetTransform((Display::MatrixType)iScript->getInt(1));
}

API_DeclareFunction(Transform)
{
  Display::DisplayDevice& wDevice = *Frame::get()->getDisplay();

  wDevice.transform(
    Vector::Vector2D(
    (REAL)iScript->getInt(1),    // scale center x
    (REAL)iScript->getInt(2)),   // scale center y
    Vector::Vector2D(
    (REAL)iScript->getInt(3),    // scale x
    (REAL)iScript->getInt(4)),   // scale y
    Vector::Vector2D(
    (REAL)iScript->getInt(5),    // rotate center x
    (REAL)iScript->getInt(6)),   // rotate center y
    RADIAN(iScript->getInt(7)),  // rotate
    Vector::Vector2D(
    (REAL)iScript->getInt(8),    // translate x
    (REAL)iScript->getInt(9)),   // translate y
    (Display::MatrixType)iScript->getInt(10)); // matrix type
}

API_DeclareFunction(IsWireframe)
{
  iScript->pushBool(Frame::get()->getDisplay()->isWireframe());
}

API_DeclareFunction(SetWireframe)
{
  Frame::get()->getDisplay()->setWireframe(iScript->getBool(1));
}

API_DeclareFunction(GetTextureSize)
{
  Texture* wGraphic = NULL;

  if (GetGraphic(wGraphic, (UINT)iScript->getInt(1), GraphicTexture))
  {
    iScript->push(wGraphic->getWidth());
    iScript->push(wGraphic->getHeight());
    return;
  }

  iScript->push(0);
  iScript->push(0);
}

API_DeclareFunction(SetValue)
{
  INT wIndex = iScript->getInt(1);

  if (wIndex < (INT)gDataManager->getSize())
  {
    switch (iScript->getType(2))
    {
    case TypeString:
      gDataManager->setString(wIndex, iScript->getAnsi(2));
      break;
    case TypeNumber:
      gDataManager->setReal(wIndex, iScript->getDouble(2));
      break;
    case TypeBoolean:
      gDataManager->setInt(wIndex, iScript->getBool(2));
      break;
    default:
     API_InvalidArgument();
    }
  }
  else
  {
    API_InvalidArgument();
  }
}

API_DeclareFunction(GetValue)
{
  INT wIndex = iScript->getInt(1);

  if (wIndex < (INT)gDataManager->getSize())
  {
    switch (gDataManager->getData(wIndex).type)
    {
    case Data::DataString:
      iScript->push(gDataManager->getData(wIndex).data.string);
      return;
    case Data::DataReal:
      iScript->push(gDataManager->getData(wIndex).data.real);
      return;
    case Data::DataInteger:
      iScript->push(gDataManager->getData(wIndex).data.integer);
      return;
    }
  }

  API_InvalidArgument();
  iScript->pushNull();
}

API_DeclareFunction(GetValueBufferSize)
{
  iScript->push((INT)gDataManager->getSize());
}

API_DeclareFunction(SetValueBufferSize)
{
  INT wSize = iScript->getInt(1);

  if (wSize < 0)
  {
    API_InvalidArgument();
    return;
  }

  gDataManager->setSize((UINT)wSize);
}

API_DeclareFunction(SaveValueBuffer)
{
  iScript->pushBool(gDataManager->save(iScript->getString(1)));
}

API_DeclareFunction(LoadValueBuffer)
{
  iScript->pushBool(gDataManager->load(iScript->getString(1)));
}

API_DeclareFunction(GetDeltaTime)
{
  iScript->push(Frame::get()->getDeltaTime());
}

API_DeclareFunction(Exit)
{
  Frame::get()->stop();
}

API_DeclareFunction(RenderFlush)
{
  gGraphBuffer->getDisplay()->end();
}

API_DefineAPI(API::InitializeAPI)
{
  Scripting::InitializeAPI(iScript);

  gDataManager  = new Data::DataManager();
  gGraphBuffer  = new GraphicBuffer(Frame::get()->getDisplay());

  API_AddFunction(Run);
  API_AddFunction(Exit);
  API_AddFunction(HitTestRect);
  API_AddFunction(GetDeltaTime);
  API_AddFunction(GetCursorPos);

  iScript->add(DirectionLeft,   "LEFT");
  iScript->add(DirectionRight,  "RIGHT");
  iScript->add(DirectionUp,     "UP");
  iScript->add(DirectionDown,   "DOWN");

  iScript->add(SpriteFull,      "FULL");
  iScript->add(SpriteBody,      "BODY");
  iScript->add(SpriteHead,      "HEAD");

  API_AddObject("ValueBuffer",
    API_MakeFunction(GetValue,            "get"),
    API_MakeFunction(SetValue,            "set"),
    API_MakeFunction(GetValueBufferSize,  "getSize"),
    API_MakeFunction(SetValueBufferSize,  "setSize"),
    API_MakeFunction(LoadValueBuffer,     "load"),
    API_MakeFunction(SaveValueBuffer,     "save"));

  API_AddObject("GridMap",
    API_MakeFunction(DestroyGraphic,      "destroy"),
    API_MakeFunction(CreateGridMap,       "create"),
    API_MakeFunction(GetGridMapSize,      "getSize"),
    API_MakeFunction(GetGridMapTexture,   "getTexture"),
    API_MakeFunction(IsMapLayerVisible,   "isVisible"),
    API_MakeFunction(SetMapLayerVisible,  "setVisible"),
    API_MakeFunction(RenderGridMap,       "render"));

  API_AddObject("SpriteMap",
    API_MakeFunction(DestroyGraphic,      "destroy"),
    API_MakeFunction(CreateSpriteMap,     "create"),
    API_MakeFunction(GetSpriteMapSize,    "getSize"),
    API_MakeFunction(RenderSpriteMap,     "render"));

  API_AddObject("Font",
    API_MakeFunction(DestroyGraphic,      "destroy"),
    API_MakeFunction(CreateFont,          "create"),
    API_MakeFunction(RenderString,        "render"));

  API_AddObject("Texture",
    API_MakeFunction(DestroyGraphic,      "destroy"),
    API_MakeFunction(CreateTexture,       "create"),
    API_MakeFunction(GetTextureSize,      "getSize"),
    API_MakeFunction(RenderTexture,       "render"));

  API_AddObject("FPSCounter",
    API_MakeFunction(DestroyGraphic,      "destroy"),
    API_MakeFunction(CreateFPSCounter,    "create"),
    API_MakeFunction(CalculateFPS,        "update"),
    API_MakeFunction(GetFPS,              "getValue"));

  API_AddObject("SpriteSet",
    API_MakeFunction(DestroyGraphic,      "destroy"),
    API_MakeFunction(CreateSpriteSet,     "create"));

  API_AddObject("SpriteBuffer",
    API_MakeFunction(DestroyGraphic,      "destroy"),
    API_MakeFunction(CreateSpriteBuffer,  "create"),
    API_MakeFunction(HitTestSprite,       "hitTestSprite"),
    API_MakeFunction(HitTestMap,          "hitTestMap"),
    API_MakeFunction(GetSprite,           "get"),
    API_MakeFunction(SetSprite,           "set"),
    API_MakeFunction(SetSpriteVelocity,   "setVelocity"),
    API_MakeFunction(SetSpriteBounds,     "setBounds"),
    API_MakeFunction(SetSpriteBoundingBox,"setBoundingBox"),
    API_MakeFunction(SetSpriteAttributes, "setAttributes"),
    API_MakeFunction(GetSpriteBufferSize, "getSize"),
    API_MakeFunction(SetSpriteBufferSize, "setSize"),
    API_MakeFunction(UpdateSpriteBuffer,  "update"),
    API_MakeFunction(RenderSpriteBuffer,  "render"));

  API_AddObject("Display",
    API_MakeFunction(GetDisplayMode,      "getMode"),
    API_MakeFunction(SetDisplayMode,      "setMode"),
    API_MakeFunction(GetScreenSize,       "getSize"),
    API_MakeFunction(Transform,           "transform"),
    API_MakeFunction(ResetTransform,      "reset"),
    API_MakeFunction(RenderFlush,         "flush"),
    API_MakeFunction(RenderRect,          "fill"),
    API_MakeFunction(IsWireframe,         "isWireframe"),
    API_MakeFunction(SetWireframe,        "setWireframe"));
}

VOID API::UninitializeAPI()
{
  SafeDelete(gGraphBuffer);
  SafeDelete(gDataManager);
}
